home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Fun, Tricks & Hacks / Silent Alarm, not! / SonicAlarm.c < prev    next >
C/C++ Source or Header  |  1992-06-18  |  46KB  |  1,618 lines

  1. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  2.  
  3. // This application will use Sound Input and Output to hopefully act 
  4. // like a car alarm.  It samples the surrounding volume and it a sound 
  5. // that is considered too loud is found while the alarm is on, it will 
  6. // play an alarm sound.
  7. // 
  8. // Since I started using so many System 7 features, and I know that 
  9. // it's not likely they'll ever make their way into an older system, I 
  10. // decided to only run on a System 7 or later machine.  Sorry if you 
  11. // hate System 7, but too bad for you cause you're missing the future.  
  12. // Another feature that's required is level metering, which is 
  13. // standard for the Apple built-in sound input device.  It's also 
  14. // works much better in color.
  15. // 
  16. // Something else you'll notice is that I have a state machine to run 
  17. // this application.  I know it's not as clean of code as I usually 
  18. // would release, but this is the best version I had ready for MacHack 
  19. // '92.  I also hate the Dialog Manager and using the windows that I 
  20. // have here would have been a royal pain (in my expert opinion) if I 
  21. // had tried using dialogs.  Instead I used standard windows and drive 
  22. // everything from the standard event loop.  I even implement modal 
  23. // dialogs this way.
  24. // 
  25. // This if the first version I've ever released, and by the time 
  26. // you're reading this I've probably found bugs and fixed them.
  27. // 
  28. // Jim Reekes, Polterzeitgeist
  29. //                           
  30. // Apple Computer, Inc.        
  31. // 20525 Mariani Ave. MS: 81-KS
  32. // Cupertino, CA 95014         
  33. // 
  34. // AppleLink: REEKES
  35.  
  36.  
  37.  
  38. // include the Think headers or the MPW ones
  39. // I always change my Think headers from the standard one shipped with Think.
  40. // 
  41. #ifdef THINK_C
  42.     #include <MacHeaders>
  43. #else
  44.     #pragma load "MPWHeaders"
  45. #endif
  46.  
  47. #include <Palettes.h>
  48.  
  49. #ifndef __SONICALARM__
  50. #include "SonicAlarm.h"
  51. #endif
  52.  
  53. #ifndef __SONICALARMMISC__
  54. #include "SonicAlarmMisc.h"
  55. #endif
  56.  
  57.  
  58. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  59. // The "g" prefix is used to emphasize that a variable is global.
  60.  
  61.  
  62. // state of the alarm (on, off, pending, etc.)
  63. short            gAlarmState;
  64.  
  65.  
  66. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  67. // prototypes
  68.  
  69. void            main(void);
  70. void            AssertFeatures(void);
  71. void            Initialize(void);
  72. void            Terminate(void);
  73.  
  74. void            EventLoop(void);
  75. void            DoEvent(EventRecord *event);
  76. void            DoUpdate(WindowPtr window);
  77. void            DoActivate(WindowPtr window, Boolean becomingActive);
  78. void            DoContentClick(WindowPtr window, EventRecord *event);
  79. void            DoIdle(void);
  80. void            AdjustMenus(Boolean updateMBar);
  81. void            DoMenuCommand(long menuResult);
  82. void            DoKeyDown(WindowPtr window, unsigned char key, short modifiers);
  83. void            CloseMyWindow(void *window);
  84.  
  85. void            CreateAlarmWindow(void);
  86. void            DrawAlarmWindow(AlarmWindowPtr alarm);
  87. void            UpdateAlarmActivated(AlarmWindowPtr alarm);
  88. void            AlarmWindowClick(WindowPtr window, EventRecord *event);
  89. void            AlarmWindowKeyDown(AlarmWindowPtr alarm, unsigned char key, short modifiers);
  90.  
  91. void            ConfigureAlarm(void);
  92. void            DrawConfigureWindow(ConfigurationWindowPtr window);
  93. void            DrawSensitivityCntl(ConfigurationWindowPtr configure);
  94. void            TrackSensitivityCntl(ConfigurationWindowPtr configure, Point mouse);
  95. void            UpdateTestLight(ConfigurationWindowPtr configure, Boolean on);
  96. void            UpdateLevels(ConfigurationWindowPtr window);
  97. void            ConfiguringWindowClick(ConfigurationWindowPtr configure, EventRecord *event);
  98. void            ConfigurationKeyDown(ConfigurationWindowPtr configure, unsigned char key, short modifiers);
  99.  
  100. void            SetPassword(void);
  101. void            DrawSetPassword(SetPasswordWindowPtr setPassword);
  102. void            DoPasswordOKButton(SetPasswordWindowPtr setPassword);
  103. void            SetPasswordClick(SetPasswordWindowPtr setPassword, EventRecord *event);
  104. void            SetPasswordKeyDown(SetPasswordWindowPtr setPassword, unsigned char key, short modifiers);
  105.  
  106. void            ActivateAlarm(void);
  107. void            DeactivateAlarm(void);
  108. void            GetPassword(void);
  109. Boolean            IsPasswordValid(DeactivationWindowPtr deactivation);
  110. void            DeactivationOKButton(DeactivationWindowPtr deactivation);
  111. void            DeactivationWindowClick(DeactivationWindowPtr deactivation, EventRecord *event);
  112.  
  113. void            DrawActivationWindow(ActivationWindowPtr activation);
  114. void            ActivatingWindowClick(WindowPtr window, EventRecord *event);
  115. void            ActivationWindowKeyDown(ActivationWindowPtr activation, unsigned char key, short modifiers);
  116.  
  117. void            DrawDeactivationWindow(DeactivationWindowPtr deactivation);
  118. void            DeactivationKeyDown(DeactivationWindowPtr deactivation, unsigned char key, short modifiers);
  119.  
  120.  
  121. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  122. void main(void)
  123. {
  124.     MaxApplZone();                    // expand the heap so code segments load at the top
  125.     Initialize();                    // initialize the program
  126.     EventLoop();                    // call the main event loop
  127. }
  128.  
  129. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  130. // check for any and all of the necessary features I need to use, such as _WaitNextEvent
  131. // at this point nothing can be relied upon, the toolbox may not have been inited yet
  132.  
  133. void AssertFeatures(void)
  134. {
  135.     long            response;
  136.     OSErr            err;
  137.  
  138.     // we'll be using DeviceLoop and GetGray, so it better be System 7.0
  139.     err = Gestalt(gestaltSystemVersion, &response);
  140.     if ( (err != noErr) || (response < kSystemSeven) )
  141.         AlertUser(noErr, eFeatureNotAvailable, kFatalError);
  142.  
  143.     if ( !HasSoundInput() )
  144.         AlertUser(noErr, eNoSndInDevice, kFatalError);
  145. }
  146.  
  147. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  148. // initialize all of the globals, the toolbox, and other things my app needs
  149.  
  150. void Initialize()
  151. {
  152.     Handle            menuBar;
  153.     EventRecord     event;
  154.     short            count;
  155.     OSErr            err;
  156.  
  157.     gAlarmState = kDeactivatedState;
  158.     SetSensitivity(kDefaultSensitivity);
  159.  
  160.     InitGraf(&qd.thePort);
  161.     InitFonts();
  162.     InitWindows();
  163.     InitMenus();
  164.     TEInit();
  165.     InitDialogs(nil);
  166.     InitCursor();
  167.  
  168.     // pull the application to the front layer
  169.     for (count = 1; count <= 3; count++)
  170.         EventAvail(everyEvent, &event);
  171.  
  172.     AssertFeatures();
  173.  
  174.     menuBar = GetNewMBar(rMenuBar);
  175.     if (menuBar == nil)
  176.         AlertUser(MemError(), eNoMemory, kFatalError);
  177.  
  178.     SetMenuBar(menuBar);
  179.     DisposHandle(menuBar);
  180.     AddResMenu(GetMHandle(mApple), 'DRVR');
  181.     DrawMenuBar();
  182.  
  183.     err = InitSndTools();
  184.     if (err != noErr)
  185.         AlertUser(err, eFatalErr, kFatalError);
  186.  
  187.     err = CreateLevelArray();
  188.     if (err != noErr)
  189.         AlertUser(err, eFatalErr, kFatalError);
  190.  
  191.     // test for minimal memory requirements after allocating application's stuff
  192.     if (FailLowMemory(0))
  193.         AlertUser(noErr, eNoMemory, kFatalError);
  194. }
  195.  
  196. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  197. // do any clean up before we die
  198.  
  199. void Terminate()
  200. {
  201.     WindowPtr    aWindow;
  202.  
  203.     do {
  204.         aWindow = FrontWindow();                // get the current front window
  205.         if (aWindow != nil)
  206.             CloseMyWindow(aWindow);                // close this window
  207.     } while (aWindow != nil);
  208.     CloseSndTools();
  209.     ExitToShell();                                // exit if no cancellation
  210. }
  211.  
  212. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  213. // loop forever checking for events to handle, WaitNextEvent will return false if
  214. // there is no events (nullEvents are not returned)
  215.  
  216. void EventLoop(void)
  217. {
  218.     EventRecord    event;
  219.  
  220.     do {
  221.         if (WaitNextEvent(everyEvent, &event, GetSleep(), nil))
  222.             DoEvent(&event);
  223.         else
  224.             DoIdle();
  225.     } while (true);
  226. }
  227.  
  228. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  229. void DoEvent(EventRecord *event)
  230. {
  231.     WindowPtr        window;
  232.     long            menuResult;
  233.     short            part;
  234.     short            err;
  235.     unsigned char    key;
  236.  
  237.     switch (event->what) {
  238.  
  239.         case mouseDown:
  240.             part = FindWindow(event->where, &window);
  241.  
  242.             // for modal windows we only handle clicks in the window and in the menu
  243.             if ( IsWindowModal(FrontWindow())
  244.              && (part != inMenuBar)
  245.              && (part != inContent) )
  246.             {
  247.                 SysBeep(3);
  248.                 break;
  249.             }
  250.  
  251.             switch (part) {
  252.  
  253.                 case inMenuBar:
  254.                     AdjustMenus(false);
  255.                     menuResult = MenuSelect(event->where);
  256.                     if (HighWord(menuResult) != 0)
  257.                         DoMenuCommand(menuResult);
  258.                     HiliteMenu(0);
  259.                     break;
  260.  
  261.                 case inSysWindow:
  262.                     SystemClick(event, window);
  263.                     break;
  264.  
  265.                 case inContent:
  266.                     if (window != FrontWindow()) {
  267.                         if (IsWindowModal(FrontWindow()))
  268.                             SysBeep(3);
  269.                         else
  270.                             SelectWindow(window);
  271.                     }
  272.                     else
  273.                         DoContentClick(window, event);
  274.                     break;
  275.  
  276.                 case inDrag:
  277.                     DragWindow(window, event->where, &qd.screenBits.bounds);
  278.                     break;
  279.                 }
  280.             break;
  281.  
  282.         case keyDown:
  283.         case autoKey:
  284.  
  285.             // handle a command key for menus, or give it to the front window
  286.             key = event->message & charCodeMask;
  287.             if (event->modifiers & cmdKey) {
  288.                 AdjustMenus(false);
  289.                 menuResult = MenuKey(key);
  290.                 if (HighWord(menuResult) != 0) {
  291.                     if (event->what != autoKey) {
  292.                         DoMenuCommand(menuResult);
  293.                         HiliteMenu(0);
  294.                     }
  295.                 }
  296.                 else
  297.                     DoKeyDown(FrontWindow(), key, event->modifiers);
  298.             }
  299.             else
  300.                 DoKeyDown(FrontWindow(), key, event->modifiers);
  301.             break;
  302.  
  303.         case updateEvt:
  304.             DoUpdate((WindowPtr) event->message);
  305.             break;
  306.  
  307.         case diskEvt:
  308.             if (HighWord(event->message) != noErr) {
  309.                 err = DIBadMount(*(PointPtr)kDITopLeft, event->message);
  310.             }
  311.             break;
  312.  
  313.         case activateEvt:
  314.             DoActivate((WindowPtr)event->message, event->modifiers & activeFlag);
  315.             break;
  316.  
  317.         case osEvt:
  318.             switch (event->message >> 24) {
  319.  
  320.                 case suspendResumeMessage:
  321.                     // suspend/resume is also an activate/deactivate
  322.                     DoActivate(FrontWindow(), event->message & resumeFlag);
  323.                     break;
  324.             }
  325.             break;
  326.     }
  327. }
  328.  
  329. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  330. void DoUpdate(WindowPtr window)
  331. {
  332.     SetPort(window);
  333.     BeginUpdate(window);                        // this sets up the visRgn
  334.     if (!EmptyRgn(window->visRgn)) {            // draw if updating needs to be done
  335.  
  336.         switch (GetMyWindowType(window)) {
  337.  
  338.             case kAlarmWindow:
  339.                 DrawAlarmWindow((AlarmWindowPtr)window);
  340.                 break;
  341.  
  342.             case kActivationWindow:
  343.                 DrawActivationWindow((ActivationWindowPtr)window);
  344.                 break;
  345.  
  346.             case kConfigurationWindow:
  347.                 DrawConfigureWindow((ConfigurationWindowPtr)window);
  348.                 break;
  349.  
  350.             case kDeactivationWindow:
  351.                 DrawDeactivationWindow((DeactivationWindowPtr)window);
  352.                 break;
  353.  
  354.             case kSetPasswordWindow:
  355.                 DrawSetPassword((SetPasswordWindowPtr)window);
  356.                 break;
  357.         }
  358.     }
  359.     EndUpdate(window);
  360. }
  361.  
  362. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  363. void DoActivate(WindowPtr window, Boolean becomingActive)
  364. {
  365.     if (window == nil)
  366.         return;
  367.  
  368.     SetPort(window);
  369.     ActivateControls(window, becomingActive);
  370.  
  371.     // do any additional activation shit, like outlines for default buttons
  372.     switch (GetMyWindowType(window)) {
  373.  
  374.         case kConfigurationWindow:
  375.             DeviceLoop(window->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
  376.                                 (long)(FindMyControl(window, kConfigurationOK)), 0);
  377.             break;
  378.  
  379.         case kDeactivationWindow:
  380.             DeviceLoop(window->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
  381.                                 (long)(FindMyControl(window, kDeactivationOK)), 0);
  382.             break;
  383.  
  384.         case kSetPasswordWindow:
  385.             DeviceLoop(window->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
  386.                                 (long)(FindMyControl(window, kSetPasswordOK)), 0);
  387.             break;
  388.     }
  389. }
  390.  
  391. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  392. void DoContentClick(WindowPtr window, EventRecord *event)
  393. {
  394.     SetPort(window);
  395.     switch (GetMyWindowType(window)) {
  396.  
  397.         case kAlarmWindow:
  398.             AlarmWindowClick(window, event);
  399.             break;
  400.  
  401.         case kActivationWindow:
  402.             ActivatingWindowClick(window, event);
  403.             break;
  404.  
  405.         case kConfigurationWindow:
  406.             ConfiguringWindowClick((ConfigurationWindowPtr)window, event);
  407.             break;
  408.  
  409.         case kDeactivationWindow:
  410.             DeactivationWindowClick((DeactivationWindowPtr)window, event);
  411.             break;
  412.  
  413.         case kSetPasswordWindow:
  414.             SetPasswordClick((SetPasswordWindowPtr)window, event);
  415.             break;
  416.     }
  417. }
  418.  
  419. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  420. // do all of the idle time handling base on the application's current state
  421. // I've tried to centralize the changing of the global state within this routine
  422. // to avoid bugs that could be cause by other routines which might change a global
  423.  
  424. void DoIdle(void)
  425. {
  426.     AlarmWindowPtr            alarm;
  427.     ActivationWindowPtr        activation;
  428.     DeactivationWindowPtr    deactivation;
  429.     ConfigurationWindowPtr    configuration;
  430.     OSErr                    err;
  431.  
  432.     // update the averaging level of sound volume
  433.     if (gAlarmState > kDeactivatedState)
  434.         UpdateAverageLevel();
  435.  
  436.     switch (gAlarmState) {
  437.  
  438.         case kActivatingState:
  439.         
  440.             // update the count down numbers, and when it's expired activate the alarm
  441.             activation = FindMyWindow(kActivationWindow);
  442.             FailIf(activation == nil, Failure);
  443.             SetPort((WindowPtr)activation);
  444.             DrawCountDown((WindowPtr)activation, activation->delayPt,
  445.                             &activation->lastTickCount, &activation->secondsCountDown);
  446.  
  447.             if (activation->secondsCountDown < 0) {
  448.                 CloseMyWindow(activation);
  449.                 CreateAlarmWindow();
  450.                 gAlarmState = kActivatedState;
  451.                 err = PlaySound(kAlarmActivatedSnd);
  452.                 FailIf(err != noErr, Failure);
  453.             }
  454.             break;
  455.  
  456.         case kDeactivatingState:
  457.         
  458.             // update the count down numbers, and if it's expired activate the alarm
  459.             // the intention is that the user should have properly turned off the
  460.             // alarm by selecting the OK button in the window
  461.             deactivation = FindMyWindow(kDeactivationWindow);
  462.             FailIf(deactivation == nil, Failure);
  463.  
  464.             SetPort((WindowPtr)deactivation);
  465.             DrawCountDown((WindowPtr)deactivation, deactivation->delayPt,
  466.                             &deactivation->lastTickCount, &deactivation->secondsCountDown);
  467.  
  468.             if (deactivation->secondsCountDown < 0) {
  469.                 CloseMyWindow(deactivation);
  470.                 err = PlaySound(kAlarmTriggeredSnd);
  471.                 FailIf(err != noErr, Failure);
  472.             }
  473.             break;
  474.  
  475.         case kConfiguringState:
  476.         
  477.             // this window is used to set the alarm's configuration, so update the
  478.             // levels indicator which shows the user the current volume level
  479.             configuration = FindMyWindow(kConfigurationWindow);
  480.             FailIf(configuration == nil, Failure);
  481.             SetPort((WindowPtr)configuration);
  482.             UpdateLevels(configuration);
  483.             UpdateTestLight(configuration, IsVolumeTooLoud());
  484.             break;
  485.  
  486.         case kActivatedState:
  487.  
  488.             // the alarm is on, so check for a loud sound which will turn on the alarm
  489.             if (IsVolumeTooLoud()) {
  490.                 err = PlaySound(kAlarmTriggeredSnd);
  491.                 FailIf(err != noErr, Failure);
  492.             }
  493.  
  494.             // continue to flash the alarm message, telling intruders the alarm is on
  495.             alarm = FindMyWindow(kAlarmWindow);
  496.             FailIf(alarm == nil, Failure);
  497.             SetPort((WindowPtr)alarm);
  498.             UpdateAlarmActivated(alarm);
  499.             break;
  500.     }
  501.  
  502. Failure:
  503.     return;
  504. }
  505.  
  506. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  507. // To disable or enable an entire menu, pass 0 as the item
  508. // We need to call DrawMenuBar when ever changing the state of an entire menu
  509. // and this would happen if the user hadn't used a menu command, which calls
  510. // HiliteMenu and that screws up the appearance of the menu.  This appears to be
  511. // a bug in the Menu Manager, because is doesn't notice that the menu it's
  512. // unhiliting has been disabled and redrawn.  The hilite logic simple inverses
  513. // the menu title area, and this then makes it look enabled.  If I always call
  514. // HiliteMenu here it tricks the Menu Manager to do the right thing.
  515.  
  516. void AdjustMenus(Boolean updateMBar)
  517. {
  518.     Boolean        isModal;
  519.     MenuHandle    menu;
  520.  
  521.     HiliteMenu(0);
  522.     isModal = IsWindowModal(FrontWindow()) || (gAlarmState == kActivatedState);
  523.  
  524.     menu = GetMHandle(mApple);
  525.     if (isModal)
  526.         DisableItem(menu, 0);
  527.     else
  528.         EnableItem(menu, 0);
  529.  
  530.     menu = GetMHandle(mFile);
  531.     if (isModal)
  532.         DisableItem(menu, 0);
  533.     else
  534.         EnableItem(menu, 0);
  535.  
  536.     menu = GetMHandle(mEdit);
  537.     if (isModal)
  538.         DisableItem(menu, 0);
  539.     else
  540.         EnableItem(menu, 0);
  541.  
  542.     menu = GetMHandle(mAlarm);
  543.     if (isModal)
  544.         DisableItem(menu, 0);
  545.     else
  546.         EnableItem(menu, 0);
  547.  
  548.     if (updateMBar)
  549.         DrawMenuBar();
  550. }
  551.  
  552. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  553. void DoMenuCommand(long menuResult)
  554. {
  555.     Str255        daName;
  556.     short        menuItem;
  557.  
  558.     menuItem = LowWord(menuResult);
  559.     switch (HighWord(menuResult)) {
  560.  
  561.         case mApple:
  562.  
  563.             switch (menuItem) {
  564.  
  565.                 case iAbout:
  566.                     DoActivate(FrontWindow(), false);
  567.                     Alert(rAboutAlert, nil);
  568.                     break;
  569.  
  570.                 default:
  571.                     GetItem(GetMHandle(mApple), menuItem, daName);
  572.                     OpenDeskAcc(daName);
  573.                     break;
  574.             }
  575.             break;
  576.  
  577.         case mFile:
  578.  
  579.             switch (menuItem) {
  580.  
  581.                 case iQuit:
  582.                     Terminate();
  583.                     break;
  584.             }
  585.             break;
  586.  
  587.         case mAlarm:
  588.  
  589.             switch (menuItem) {
  590.  
  591.                 case iSetPassword:
  592.                     SetPassword();
  593.                     break;
  594.  
  595.                 case iActivate:
  596.                     ConfigureAlarm();
  597.                     break;
  598.             }
  599.             break;
  600.     }
  601. }
  602.  
  603. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  604. // dispatch to the window's handler passing it the key and modifiers
  605.  
  606. void DoKeyDown(WindowPtr window, unsigned char key, short modifiers)
  607. {
  608.     switch (GetMyWindowType(window)) {
  609.  
  610.         case kAlarmWindow:
  611.             AlarmWindowKeyDown((AlarmWindowPtr)window, key, modifiers);
  612.             break;
  613.  
  614.         case kActivationWindow:
  615.             ActivationWindowKeyDown((ActivationWindowPtr)window, key, modifiers);
  616.             break;
  617.  
  618.         case kDeactivationWindow:
  619.             DeactivationKeyDown((DeactivationWindowPtr)window, key, modifiers);
  620.             break;
  621.  
  622.         case kConfigurationWindow:
  623.             ConfigurationKeyDown((ConfigurationWindowPtr)window, key, modifiers);
  624.             break;
  625.  
  626.         case kSetPasswordWindow:
  627.             SetPasswordKeyDown((SetPasswordWindowPtr)window, key, modifiers);
  628.             break;
  629.     }
  630. }
  631.  
  632. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  633. // Close a window. This handles desk accessory and any application windows.
  634.  
  635. void CloseMyWindow(void *window)
  636. {
  637.     Boolean        isModal;
  638.  
  639.     if (IsDAWindow(window))
  640.         CloseDeskAcc(((WindowPeek)window)->windowKind);
  641.     else {
  642.  
  643.         switch (GetWRefCon(window)) {
  644.  
  645.             case kAlarmWindow:
  646.                 if (((AlarmWindowPtr)window)->message != nil)
  647.                     ReleaseResource((Handle)((AlarmWindowPtr)window)->message);
  648.                 break;
  649.  
  650.             case kActivationWindow:
  651.                 if (((ActivationWindowPtr)window)->message != nil)
  652.                     ReleaseResource((Handle)((ActivationWindowPtr)window)->message);
  653.                 break;
  654.  
  655.             case kConfigurationWindow:
  656.                 if (((ConfigurationWindowPtr)window)->message != nil)
  657.                     ReleaseResource((Handle)((ConfigurationWindowPtr)window)->message);
  658.                 if (((ConfigurationWindowPtr)window)->sensitivityTitle != nil)
  659.                     ReleaseResource((Handle)((ConfigurationWindowPtr)window)->sensitivityTitle);
  660.                 if (((ConfigurationWindowPtr)window)->max != nil)
  661.                     ReleaseResource((Handle)((ConfigurationWindowPtr)window)->max);
  662.                 if (((ConfigurationWindowPtr)window)->min != nil)
  663.                     ReleaseResource((Handle)((ConfigurationWindowPtr)window)->min);
  664.                 break;
  665.  
  666.             case kDeactivationWindow:
  667.                 if (((DeactivationWindowPtr)window)->message != nil)
  668.                     ReleaseResource((Handle)((DeactivationWindowPtr)window)->message);
  669.                 break;
  670.  
  671.             case kSetPasswordWindow:
  672.                 if (((SetPasswordWindowPtr)window)->message != nil)
  673.                     ReleaseResource((Handle)((SetPasswordWindowPtr)window)->message);
  674.                 break;
  675.         }
  676.         isModal = IsWindowModal(window);
  677.         CloseWindow(window);
  678.         if (isModal)
  679.             AdjustMenus(true);
  680.         DisposePtr(window);
  681.     }
  682. }
  683.  
  684. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  685. // this window is all black and will cover all screens, the message area is drawn
  686. // onto the main screen's area
  687.  
  688. void CreateAlarmWindow(void)
  689. {
  690.     AlarmWindowPtr        alarm;
  691.     Rect                bigRect;
  692.  
  693.     alarm = GetMyWindow(sizeof(AlarmWindow), kAlarmWindow);
  694.     FailIf(alarm == nil, NoWindow)
  695.  
  696.     alarm->message = GetString(kAlarmMessage);
  697.     FailIf(alarm->message == nil, NoString)
  698.  
  699.     // nasty way to get a rect that covers all screens
  700.     bigRect = (**(*(RgnHandle *)GrayRgn)).rgnBBox;
  701.     SizeWindow((WindowPtr)&alarm->window, bigRect.right - bigRect.left,
  702.                                         bigRect.bottom - bigRect.top, false);
  703.     MoveWindow((WindowPtr)&alarm->window, bigRect.left, bigRect.top, true);
  704.  
  705.     // get the main area for drawing the screen, below the menubar
  706.     alarm->msgRect = qd.screenBits.bounds;
  707.     GlobalToLocal(&TopLeft(alarm->msgRect));
  708.     GlobalToLocal(&BottomRight(alarm->msgRect));
  709.     alarm->msgRect.top += GetMBarHeight() + kSpaceBelowMBar;
  710.  
  711.     SetPort((WindowPtr)alarm);
  712.     ForeColor(whiteColor);
  713.     BackColor(blackColor);
  714.     ShowWindow((WindowPtr)alarm);
  715.     EraseRect(&((WindowPtr)alarm)->portRect);            // erase it all black quickly
  716.     return;
  717.  
  718. NoString:
  719.     CloseMyWindow(alarm);
  720. NoWindow:
  721.     AlertUser(MemError(), eNoWindow, kFatalError);
  722. }
  723.  
  724. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  725. // need to correctly update the main screen based on the current alarm state
  726. // which is just all black and since the back color is set to black we only have
  727. // to call erase rect
  728.  
  729. void DrawAlarmWindow(AlarmWindowPtr alarm)
  730. {
  731.     EraseRect(&((WindowPtr)alarm)->portRect);
  732. }
  733.  
  734. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  735. // the alarm is on, so draw the warning message
  736.  
  737. void UpdateAlarmActivated(AlarmWindowPtr alarm)
  738. {
  739.     long        curTicks;
  740.  
  741.     curTicks = TickCount();
  742.     if (alarm->lastTickCount + kOneSecondTicks <= curTicks) {
  743.  
  744.         alarm->lastTickCount = curTicks;
  745.         SetPort((WindowPtr)&alarm->window);
  746.         if (alarm->drawWhite)
  747.             ForeColor(whiteColor);
  748.         else
  749.             ForeColor(redColor);
  750.         alarm->drawWhite = !alarm->drawWhite;
  751.         TextFont(kAlarmFontID);
  752.         TextSize(kAlarmFontSize);
  753.  
  754.         HLock((Handle)alarm->message);
  755.         TextBox(*alarm->message + 1, **alarm->message, &alarm->msgRect, teJustCenter);
  756.         HUnlock((Handle)alarm->message);
  757.     }
  758. }
  759.  
  760. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  761. void AlarmWindowClick(WindowPtr window, EventRecord *event)
  762. {
  763.     Point            mouse;
  764.  
  765.     SetPort(window);
  766.     mouse = event->where;                            // get the click position
  767.     GlobalToLocal(&mouse);
  768. }
  769.  
  770. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  771. // space bar is used to deactiate the alarm
  772.  
  773. void AlarmWindowKeyDown(AlarmWindowPtr alarm, unsigned char key, short modifiers)
  774. {
  775. #pragma unused (alarm, modifiers)
  776.  
  777.     switch (key) {
  778.  
  779.         case charSpace:
  780.             StopPlaying();
  781.             DeactivateAlarm();
  782.             break;
  783.     }
  784. }
  785.  
  786. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  787. // create the configuration window
  788.  
  789. void ConfigureAlarm(void)
  790. {
  791.     ConfigurationWindowPtr    configure;
  792.     ControlHandle            control;
  793.     RectPtr                    *rectHandle;
  794.     short                    i;
  795.  
  796.     // first look if this window is alreay open, and if so just select it
  797.     configure = FindMyWindow(kConfigurationWindow);
  798.     if (configure != nil) {
  799.         SelectWindow((WindowPtr)configure);
  800.         return;
  801.     }
  802.  
  803.     configure = GetMyWindow(sizeof(ConfigurationWindow), kConfigurationWindow);
  804.     FailIf(configure == nil, NoWindow)
  805.  
  806.     configure->message = GetString(kConfigureMessage);
  807.     FailIf(configure->message == nil, NoString);
  808.  
  809.     configure->sensitivityTitle = GetString(kSensitivityTitle);
  810.     FailIf(configure->sensitivityTitle == nil, NoString);
  811.  
  812.     configure->max = GetString(kSensitivityMax);
  813.     FailIf(configure->max == nil, NoString);
  814.  
  815.     configure->min = GetString(kSensitivityMin);
  816.     FailIf(configure->min == nil, NoString);
  817.  
  818.     rectHandle = (RectPtr *)GetResource('RECT', kConfigureLevelRect);
  819.     FailIf(rectHandle == nil, NoRect);
  820.     configure->levelRect = **rectHandle;
  821.  
  822.     rectHandle = (RectPtr *)GetResource('RECT', kTestLightRect);
  823.     FailIf(rectHandle == nil, NoRect);
  824.     configure->testLightRect = **rectHandle;
  825.  
  826.     rectHandle = (RectPtr *)GetResource('RECT', kSensitivityRect);
  827.     FailIf(rectHandle == nil, NoRect);
  828.     configure->sensitivityRect = **rectHandle;
  829.  
  830.     rectHandle = (RectPtr *)GetResource('RECT', kConfigureMsgRect);
  831.     FailIf(rectHandle == nil, NoRect);
  832.     configure->msgRect = **rectHandle;
  833.  
  834.     control = GetMyControl(kConfigurationCancel, (WindowPtr)configure);
  835.     FailIf(control == nil, NoControl);
  836.  
  837.     control = GetMyControl(kConfigurationOK, (WindowPtr)configure);
  838.     FailIf(control == nil, NoControl);
  839.  
  840.     // need to initialize the levels for averaging
  841.     for (i = kNumberOfLevels - 1; i >= 0; --i)
  842.         UpdateAverageLevel();
  843.  
  844.     configure->testLightOn = false;
  845.     configure->sensitivity = GetSensitivity();
  846.     SetPort((WindowPtr)configure);
  847.     TextSize(kSensitivityFontSize);
  848.     TextFont(applFont);
  849.     ShowWindow((WindowPtr)configure);
  850.     gAlarmState = kConfiguringState;
  851.     AdjustMenus(true);
  852.     return;
  853.  
  854. NoControl:
  855. NoRect:
  856. NoString:
  857.     CloseMyWindow(configure);
  858. NoWindow:
  859.     AlertUser(MemError(), eNoWindow, kFatalError);
  860. }
  861.  
  862. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  863. void DrawConfigureWindow(ConfigurationWindowPtr configure)
  864. {
  865.     Rect        frame;
  866.  
  867.     PenNormal();
  868.     frame = configure->levelRect;
  869.     FrameRect(&frame);
  870.     UpdateLevels(configure);
  871.     frame = configure->testLightRect;
  872.     FrameRect(&frame);
  873.     DrawSensitivityCntl(configure);
  874.     UpdateTestLight(configure, configure->testLightOn);
  875.     HLock((Handle)configure->message);
  876.     TextBox(*configure->message + 1, **configure->message, &configure->msgRect, teJustLeft);
  877.     HUnlock((Handle)configure->message);
  878.     UpdateControls((WindowPtr)configure, ((WindowPtr)configure)->visRgn);
  879.     DeviceLoop(((WindowPtr)configure)->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
  880.                 (long)(FindMyControl((WindowPtr)configure, kConfigurationOK)), 0);
  881. }
  882.  
  883. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  884. // draw the percentage bar, 0% at the bottom and 100% at the top
  885.  
  886. void DrawSensitivityCntl(ConfigurationWindowPtr configure)
  887. {
  888.     FontInfo    info;
  889.     RGBColor    backColor;
  890.     RGBColor    foreColor;
  891.     RGBColor    grayColor;
  892.     Rect        sensitivityRect;
  893.     Rect        globalRect;
  894.     short        sensitivityWidth;
  895.     short        titleWidth;
  896.     short        newTop;
  897.     Boolean        isColor;
  898.  
  899.     sensitivityRect = configure->sensitivityRect;
  900.     sensitivityWidth = sensitivityRect.right - sensitivityRect.left;
  901.  
  902.     HLock((Handle)configure->sensitivityTitle);
  903.     titleWidth = StringWidth(*configure->sensitivityTitle);
  904.  
  905.     // move to above the control a little, and center it according to the widths
  906.     MoveTo((sensitivityRect.left + (sensitivityWidth >> 1)) - (titleWidth >> 1),
  907.             sensitivityRect.top - kSensitivitySpace);
  908.     DrawString(*configure->sensitivityTitle);
  909.     HUnlock((Handle)configure->sensitivityTitle);
  910.  
  911.     GetFontInfo(&info);
  912.     // move to the top of the control, but draw the text under the top
  913.     MoveTo(sensitivityRect.right + kSensitivitySpace,
  914.             sensitivityRect.top + info.ascent + info.descent + info.leading);
  915.     HLock((Handle)configure->max);
  916.     DrawString(*configure->max);
  917.     HUnlock((Handle)configure->max);
  918.  
  919.     // move to the bottom of the control
  920.     MoveTo(sensitivityRect.right + kSensitivitySpace, sensitivityRect.bottom);
  921.     HLock((Handle)configure->min);
  922.     DrawString(*configure->min);
  923.     HUnlock((Handle)configure->min);
  924.  
  925.     FrameRect(&sensitivityRect);
  926.     InsetRect(&sensitivityRect, 1, 1);
  927.  
  928.     newTop = sensitivityRect.bottom - sensitivityRect.top;
  929.     newTop = (newTop * (100 - configure->sensitivity)) / 100;
  930.     sensitivityRect.top = sensitivityRect.bottom - newTop;
  931.     
  932.     // read the warning about using patterns in the MPW E.T.O #6 CD ROM
  933.     // they were defined as an array, but should have been a struct
  934.     // the standard Think C includes is out of date, but I'm using the ETO #6 release
  935.     FillRect(&sensitivityRect, &qd.black);
  936.  
  937.     sensitivityRect.bottom = sensitivityRect.top;
  938.     sensitivityRect.top = configure->sensitivityRect.top + 1;
  939.  
  940.     // check for a color window
  941.     if (((CGrafPtr)configure)->portVersion & kIsColorPort) {
  942.         isColor = true;
  943.         GetBackColor(&backColor);
  944.         GetForeColor(&foreColor);
  945.         grayColor = foreColor;
  946.         globalRect = sensitivityRect;
  947.         LocalToGlobal(&TopLeft(globalRect));
  948.         LocalToGlobal(&BottomRight(globalRect));
  949.     }
  950.     else
  951.         isColor = false;
  952.  
  953.     // find a nice gray for color windows, otherwise use a gray pattern
  954.     if ((isColor) && GetGray(GetRectDevice(globalRect), &backColor, &grayColor)) {
  955.         RGBForeColor(&grayColor);
  956.         PaintRect(&sensitivityRect);
  957.         RGBForeColor(&foreColor);
  958.     }
  959.     else
  960.         FillRect(&sensitivityRect, &qd.ltGray);
  961. }
  962.  
  963. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  964. // the user is dragging the sensitivity level control, so track this until their done
  965.  
  966. void TrackSensitivityCntl(ConfigurationWindowPtr configure, Point mouse)
  967. {
  968.     short        sensitivity;
  969.     Point        newPosition;
  970.  
  971.     newPosition = mouse;
  972.     while (StillDown() && PtInRect(newPosition, &configure->sensitivityRect)) {
  973.  
  974.         sensitivity = (newPosition.v - configure->sensitivityRect.top) * 100;
  975.         sensitivity /= configure->sensitivityRect.bottom - configure->sensitivityRect.top;
  976.         configure->sensitivity = sensitivity;
  977.         SetSensitivity(sensitivity);
  978.  
  979.         DrawSensitivityCntl(configure);
  980.         GetMouse(&newPosition);
  981.     }
  982. }
  983.  
  984. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  985. // this light will flash when the alarm would have been turned on, and there should
  986. // be a short amount of delay to let the user see it
  987.  
  988. void UpdateTestLight(ConfigurationWindowPtr configure, Boolean on)
  989. {
  990.     Rect        frame;
  991.  
  992.     frame = configure->testLightRect;
  993.     InsetRect(&frame, 1, 1);
  994.     configure->testLightOn = on;
  995.  
  996.     ForeColor(redColor);
  997.  
  998.     if (on)
  999.         configure->lastTickCount = TickCount();
  1000.     else if (configure->lastTickCount + kVisualDelay < TickCount()) {
  1001.         ForeColor(whiteColor);
  1002.         configure->testLightOn = !on;
  1003.     }
  1004.  
  1005.     PaintRect(&frame);
  1006.     ForeColor(blackColor);
  1007. }
  1008.  
  1009. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1010. // draw the current volume levels to show the user volume feedback
  1011.  
  1012. void UpdateLevels(ConfigurationWindowPtr configure)
  1013. {
  1014.     Rect        levelRect;
  1015.     short        curLevel;
  1016.     short        averageLevel;
  1017.     short        newTop;
  1018.  
  1019.     SetPort((WindowPtr)&configure->window);
  1020.     PenNormal();
  1021.  
  1022.     curLevel = GetCurLevel();
  1023.     averageLevel = GetAverageLevel();
  1024.     levelRect = configure->levelRect;
  1025.     InsetRect(&levelRect, 1, 1);
  1026.  
  1027.     // divide by 225 (max level) quickly by shifting to the right
  1028.  
  1029.     newTop = levelRect.bottom - levelRect.top;
  1030.     newTop = (newTop * averageLevel) >> 8;
  1031.     levelRect.top = levelRect.bottom - newTop;
  1032.     ForeColor(greenColor);
  1033.     PaintRect(&levelRect);
  1034.  
  1035.     if (curLevel > averageLevel) {
  1036.  
  1037.         levelRect.bottom = levelRect.top;
  1038.         newTop = configure->levelRect.bottom - configure->levelRect.top;
  1039.         newTop = (newTop * curLevel) >> 8;
  1040.         levelRect.top = configure->levelRect.bottom - newTop;
  1041.         ForeColor(yellowColor);
  1042.         PaintRect(&levelRect);
  1043.     }
  1044.  
  1045.     ForeColor(blackColor);
  1046.     levelRect.bottom = levelRect.top;
  1047.     levelRect.top = configure->levelRect.top;
  1048.     EraseRect(&levelRect);
  1049. }
  1050.  
  1051. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1052. void ConfiguringWindowClick(ConfigurationWindowPtr configure, EventRecord *event)
  1053. {
  1054.     Point            mouse;
  1055.     ControlHandle    control;
  1056.  
  1057.     mouse = event->where;
  1058.     GlobalToLocal(&mouse);
  1059.  
  1060.     if (FindControl(mouse, (WindowPtr)configure, &control)) {
  1061.         if (TrackControl(control, mouse, nil)) {
  1062.  
  1063.             switch (GetCRefCon(control)) {
  1064.  
  1065.                 case kConfigurationCancel:
  1066.                     gAlarmState = kDeactivatedState;
  1067.                     CloseMyWindow(configure);
  1068.                     break;
  1069.  
  1070.                 case kConfigurationOK:
  1071.                     SetSensitivity(configure->sensitivity);
  1072.                     CloseMyWindow(configure);
  1073.                     ActivateAlarm();
  1074.                     break;
  1075.             }
  1076.         }
  1077.     }
  1078.     else if ( PtInRect(mouse, &configure->sensitivityRect) )
  1079.         TrackSensitivityCntl(configure, mouse);
  1080. }
  1081.  
  1082. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1083. void ConfigurationKeyDown(ConfigurationWindowPtr configure, unsigned char key, short modifiers)
  1084. {
  1085.     switch (key) {
  1086.  
  1087.         case charEnter:
  1088.         case charReturn:
  1089.             SelectButton(FindMyControl((WindowPtr)configure, kConfigurationOK));
  1090.             SetSensitivity(configure->sensitivity);
  1091.             CloseMyWindow(configure);
  1092.             ActivateAlarm();
  1093.             break;
  1094.  
  1095.         case charEscape:
  1096.             SelectButton(FindMyControl((WindowPtr)configure, kConfigurationCancel));
  1097.             gAlarmState = kDeactivatedState;
  1098.             CloseMyWindow(configure);
  1099.             break;
  1100.  
  1101.         case charPeriod:
  1102.             if (modifiers & cmdKey) {
  1103.                 SelectButton(FindMyControl((WindowPtr)configure, kConfigurationCancel));
  1104.                 gAlarmState = kDeactivatedState;
  1105.                 CloseMyWindow(configure);
  1106.             }
  1107.             break;
  1108.     }
  1109. }
  1110.  
  1111. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1112. void ActivateAlarm(void)
  1113. {
  1114.     ActivationWindowPtr        activation;
  1115.     ControlHandle            control;
  1116.     RectPtr                    *rectHandle;
  1117.  
  1118.     activation = GetMyWindow(sizeof(ActivationWindow), kActivationWindow);
  1119.     FailIf(activation == nil, NoWindow)
  1120.  
  1121.     control = GetMyControl(kCancelCountDown, (WindowPtr)activation);
  1122.     FailIf(control == nil, NoControl);
  1123.  
  1124.     rectHandle = (RectPtr *)GetResource('RECT', kActivationCountRect);
  1125.     FailIf(rectHandle == nil, NoRect);
  1126.     activation->delayPt.h = (**rectHandle).right;
  1127.     activation->delayPt.v = (**rectHandle).bottom;
  1128.  
  1129.     rectHandle = (RectPtr *)GetResource('RECT', kActivationMsgRect);
  1130.     FailIf(rectHandle == nil, NoRect);
  1131.     activation->msgRect = **rectHandle;
  1132.  
  1133.     activation->message = GetString(kActivationMessage);
  1134.     FailIf(activation->message == nil, NoString);
  1135.  
  1136.     activation->lastTickCount = TickCount();
  1137.     activation->secondsCountDown = kActivationDelay;
  1138.  
  1139.     ShowWindow((WindowPtr)activation);
  1140.     gAlarmState = kActivatingState;
  1141.     AdjustMenus(true);
  1142.     return;
  1143.  
  1144. NoString:
  1145. NoRect:
  1146. NoControl:
  1147.     CloseMyWindow(activation);
  1148. NoWindow:
  1149.     AlertUser(MemError(), eNoWindow, kFatalError);
  1150. }
  1151.  
  1152. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1153. // attempt to deactivate the alarm based on the current state
  1154.  
  1155. void DeactivateAlarm(void)
  1156. {
  1157.     AlarmWindowPtr            alarm;
  1158.     ActivationWindowPtr        activation;
  1159.     DeactivationWindowPtr    deactivation;
  1160.     OSErr                    err;
  1161.  
  1162.     switch (gAlarmState) {
  1163.  
  1164.         case kActivatingState:
  1165.             gAlarmState = kDeactivatedState;
  1166.  
  1167.             activation = FindMyWindow(kActivationWindow);
  1168.             FailIf(activation == nil, Failure);
  1169.  
  1170.             SelectButton(FindMyControl((WindowPtr)activation, kCancelCountDown));
  1171.             activation->secondsCountDown = 0;
  1172.             CloseMyWindow(activation);
  1173.             break;
  1174.  
  1175.         case kActivatedState:
  1176.             gAlarmState = kDeactivatingState;
  1177.  
  1178.             GetPassword();
  1179.             AdjustMenus(true);
  1180.             break;
  1181.  
  1182.         case kDeactivatingState:
  1183.             gAlarmState = kDeactivatedState;
  1184.  
  1185.             err = PlaySound(kAlarmDeactivatedSnd);
  1186.             FailMsg(err != noErr);
  1187.             deactivation = FindMyWindow(kDeactivationWindow);
  1188.             FailIf(deactivation == nil, Failure);
  1189.             CloseMyWindow(deactivation);
  1190.             alarm = FindMyWindow(kAlarmWindow);
  1191.             FailIf(alarm == nil, Failure);
  1192.             CloseMyWindow(alarm);
  1193.             AdjustMenus(true);
  1194.             break;
  1195.  
  1196.         default:
  1197.             FailIf("Bad State", Failure);
  1198.             break;
  1199.     }
  1200.  
  1201. Failure:
  1202.     return;
  1203. }
  1204.  
  1205. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1206. void DrawActivationWindow(ActivationWindowPtr activation)
  1207. {
  1208.     TextFont(systemFont);
  1209.     TextSize(kSystemFontSize);
  1210.     HLock((Handle)activation->message);
  1211.     TextBox(*activation->message + 1, **activation->message, &activation->msgRect, teJustLeft);
  1212.     HUnlock((Handle)activation->message);
  1213.     DrawCountDown((WindowPtr)&activation->window, activation->delayPt,
  1214.                     &activation->lastTickCount, &activation->secondsCountDown);
  1215.     UpdateControls((WindowPtr)activation, ((WindowPtr)activation)->visRgn);
  1216. }
  1217.  
  1218. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1219. void ActivatingWindowClick(WindowPtr window, EventRecord *event)
  1220. {
  1221.     Point            mouse;
  1222.     ControlHandle    control;
  1223.  
  1224.     mouse = event->where;                            // get the click position
  1225.     GlobalToLocal(&mouse);
  1226.  
  1227.     if (FindControl(mouse, window, &control)) {
  1228.         if (TrackControl(control, mouse, nil)) {
  1229.  
  1230.             switch (GetCRefCon(control)) {
  1231.  
  1232.                 case kCancelCountDown:
  1233.                     DeactivateAlarm();
  1234.                     break;
  1235.  
  1236.             }
  1237.         }
  1238.     }
  1239. }
  1240.  
  1241. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1242. void ActivationWindowKeyDown(ActivationWindowPtr activation, unsigned char key, short modifiers)
  1243. {
  1244. #pragma unused (activation)
  1245.  
  1246.     switch (key) {
  1247.  
  1248.         case charEscape:
  1249.             DeactivateAlarm();
  1250.             break;
  1251.  
  1252.         case charPeriod:
  1253.             if (modifiers & cmdKey)
  1254.                 DeactivateAlarm();
  1255.             break;
  1256.     }
  1257. }
  1258.  
  1259. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1260. void SetPassword(void)
  1261. {
  1262.     SetPasswordWindowPtr    setPassword;
  1263.     ControlHandle            control;
  1264.     RectPtr                    *rectHandle;
  1265.  
  1266.     setPassword = GetMyWindow(sizeof(SetPasswordWindow), kSetPasswordWindow);
  1267.     FailIf(setPassword == nil, NoWindow)
  1268.  
  1269.     control = GetMyControl(kSetPasswordCancel, (WindowPtr)setPassword);
  1270.     FailIf(control == nil, NoControl);
  1271.  
  1272.     control = GetMyControl(kSetPasswordOK, (WindowPtr)setPassword);
  1273.     FailIf(control == nil, NoControl);
  1274.  
  1275.     rectHandle = (RectPtr *)GetResource('RECT', kSetPasswordMsgRect);
  1276.     FailIf(rectHandle == nil, NoRect);
  1277.     setPassword->msgRect = **rectHandle;
  1278.  
  1279.     rectHandle = (RectPtr *)GetResource('RECT', kSetPasswordRect);
  1280.     FailIf(rectHandle == nil, NoRect);
  1281.     setPassword->passwordRect = **rectHandle;
  1282.  
  1283.     setPassword->message = GetString(kSetPassword1Message);
  1284.     FailIf(setPassword->message == nil, NoString);
  1285.  
  1286.     setPassword->firstPhase = true;
  1287.     setPassword->accepted = false;
  1288.     *setPassword->password1 = 0;                        // zero length string, so far
  1289.     *setPassword->password2 = 0;
  1290.     SetPort((WindowPtr)setPassword);
  1291.     TextFont(systemFont);
  1292.     TextSize(kSystemFontSize);
  1293.     ShowWindow((WindowPtr)setPassword);
  1294.     AdjustMenus(true);
  1295.     return;
  1296.  
  1297. NoString:
  1298. NoRect:
  1299. NoControl:
  1300.     CloseMyWindow(setPassword);
  1301. NoWindow:
  1302.     AlertUser(MemError(), eNoWindow, kFatalError);
  1303. }
  1304.  
  1305. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1306. void DrawSetPassword(SetPasswordWindowPtr setPassword)
  1307. {
  1308.     Rect            frame;
  1309.     unsigned char    length;
  1310.  
  1311.     frame = setPassword->passwordRect;
  1312.     FrameRect(&frame);
  1313.     HLock((Handle)setPassword->message);
  1314.     TextBox(*setPassword->message + 1, **setPassword->message, &setPassword->msgRect, teJustLeft);
  1315.     HUnlock((Handle)setPassword->message);
  1316.  
  1317.     if (setPassword->firstPhase)
  1318.         length = Length(setPassword->password1);
  1319.     else
  1320.         length = Length(setPassword->password2);
  1321.     DrawPassword(setPassword->passwordRect, length);
  1322.     UpdateControls((WindowPtr)setPassword, ((WindowPtr)&setPassword->window)->visRgn);
  1323.     DeviceLoop(((WindowPtr)setPassword)->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
  1324.                 (long)(FindMyControl((WindowPtr)setPassword, kSetPasswordOK)), 0);
  1325. }
  1326.  
  1327. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1328. // the OK button was selected, make sure the user entered the password the same
  1329. // way twice.
  1330.  
  1331. void DoPasswordOKButton(SetPasswordWindowPtr setPassword)
  1332. {
  1333.     Handle        newPassword;
  1334.     Rect        drawArea;
  1335.  
  1336.     if (setPassword->firstPhase) {
  1337.  
  1338.         // ask the user to enter the password a second time
  1339.         setPassword->firstPhase = false;
  1340.         setPassword->message = GetString(kSetPassword2Message);
  1341.         FailIf(setPassword->message == nil, Failure);
  1342.         InvalRect(&setPassword->msgRect);
  1343.         drawArea = setPassword->passwordRect;
  1344.         InsetRect(&drawArea, kEditTextInset, kEditTextInset);
  1345.         EraseRect(&drawArea);
  1346.         InvalRect(&drawArea);
  1347.     }
  1348.     else {
  1349.  
  1350.         // compare the second password, accept it or startover
  1351.         if (IUEqualString(setPassword->password1, setPassword->password2) == 0)
  1352.             setPassword->accepted = true;
  1353.         else {
  1354.  
  1355.             // start over, and do it all over again from the beginning
  1356.             DoActivate((WindowPtr)setPassword, false);
  1357.             Alert(rPasswordsDoNotMatch, nil);
  1358.             *setPassword->password1 = 0;            // zero length string, so far
  1359.             *setPassword->password2 = 0;
  1360.             setPassword->firstPhase = true;
  1361.             setPassword->message = GetString(kSetPassword1Message);
  1362.             FailIf(setPassword->message == nil, Failure);
  1363.             InvalRect(&setPassword->msgRect);
  1364.             drawArea = setPassword->passwordRect;
  1365.             InsetRect(&drawArea, kEditTextInset, kEditTextInset);
  1366.             EraseRect(&drawArea);
  1367.             InvalRect(&drawArea);
  1368.         }
  1369.     }
  1370.  
  1371.     if (setPassword->accepted) {
  1372.  
  1373.         // save the user's new password, and close the dialog window
  1374.         newPassword = GetResource(kPasswordResType, kPasswordResID);
  1375.         FailIf(newPassword == nil, Failure);
  1376.         SetHandleSize(newPassword, kLengthOfPassword);
  1377.         PascalStringCopy(setPassword->password1, (StringPtr)*newPassword);
  1378.         ChangedResource(newPassword);
  1379.         WriteResource(newPassword);
  1380.         CloseMyWindow(setPassword);
  1381.     }
  1382.  
  1383. Failure:
  1384.     return;
  1385. }
  1386.  
  1387. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1388. void SetPasswordKeyDown(SetPasswordWindowPtr setPassword, unsigned char key, short modifiers)
  1389. {
  1390.     StringPtr            password;
  1391.     unsigned char        length;
  1392.  
  1393.     if (setPassword->firstPhase)
  1394.         password = setPassword->password1;
  1395.     else
  1396.         password = setPassword->password2;
  1397.  
  1398.     switch (key) {
  1399.  
  1400.         case charBackspace:
  1401.             *password = 0;
  1402.             DrawPassword(setPassword->passwordRect, 0);
  1403.             break;
  1404.  
  1405.         case charEnter:
  1406.         case charReturn:
  1407.             SelectButton(FindMyControl((WindowPtr)setPassword, kSetPasswordOK));
  1408.             DoPasswordOKButton(setPassword);
  1409.             break;
  1410.  
  1411.         case charEscape:
  1412.             SelectButton(FindMyControl((WindowPtr)setPassword, kSetPasswordCancel));
  1413.             CloseMyWindow(setPassword);
  1414.             break;
  1415.  
  1416.         case charPeriod:
  1417.             if (modifiers & cmdKey) {
  1418.                 SelectButton(FindMyControl((WindowPtr)setPassword, kSetPasswordCancel));
  1419.                 CloseMyWindow(setPassword);
  1420.             }
  1421.             break;
  1422.  
  1423.         default:
  1424.             length = LimitStringLength(password, kLengthOfPassword);
  1425.             password[length] = key;
  1426.             SetPort((WindowPtr)setPassword);
  1427.             DrawPassword(setPassword->passwordRect, length);
  1428.             break;
  1429.     }
  1430. }
  1431.  
  1432. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1433. void SetPasswordClick(SetPasswordWindowPtr setPassword, EventRecord *event)
  1434. {
  1435.     Point            mouse;
  1436.     ControlHandle    control;
  1437.  
  1438.     mouse = event->where;                            // get the click position
  1439.     GlobalToLocal(&mouse);
  1440.  
  1441.     if (FindControl(mouse, (WindowPtr)setPassword, &control)) {
  1442.         if (TrackControl(control, mouse, nil)) {
  1443.  
  1444.             switch (GetCRefCon(control)) {
  1445.  
  1446.                 case kSetPasswordCancel:
  1447.                     CloseMyWindow(setPassword);
  1448.                     break;
  1449.  
  1450.                 case kSetPasswordOK:
  1451.                     DoPasswordOKButton(setPassword);
  1452.                     break;
  1453.  
  1454.             }
  1455.         }
  1456.     }
  1457. }
  1458.  
  1459. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1460. void GetPassword(void)
  1461. {
  1462.     DeactivationWindowPtr    deactivation;
  1463.     ControlHandle            control;
  1464.     RectPtr                    *rectHandle;
  1465.  
  1466.     deactivation = GetMyWindow(sizeof(DeactivationWindow), kDeactivationWindow);
  1467.     FailIf(deactivation == nil, NoWindow)
  1468.  
  1469.     control = GetMyControl(kDeactivationOK, (WindowPtr)deactivation);
  1470.     FailIf(control == nil, NoControl);
  1471.  
  1472.     rectHandle = (RectPtr *)GetResource('RECT', kPasswordRect);
  1473.     FailIf(rectHandle == nil, NoRect);
  1474.     deactivation->passwordRect = **rectHandle;
  1475.  
  1476.     rectHandle = (RectPtr *)GetResource('RECT', kDeactivationMsgRect);
  1477.     FailIf(rectHandle == nil, NoRect);
  1478.     deactivation->msgRect = **rectHandle;
  1479.  
  1480.     rectHandle = (RectPtr *)GetResource('RECT', kDeactivationCountRect);
  1481.     FailIf(rectHandle == nil, NoRect);
  1482.     deactivation->delayPt.h = (**rectHandle).right;
  1483.     deactivation->delayPt.v = (**rectHandle).bottom;
  1484.  
  1485.     deactivation->message = GetString(kDeactivationMessage);
  1486.     FailIf(deactivation->message == nil, NoString);
  1487.  
  1488.     deactivation->lastTickCount = TickCount();
  1489.     deactivation->secondsCountDown = kDeactivationDelay;
  1490.  
  1491.     ShowWindow((WindowPtr)deactivation);
  1492.     AdjustMenus(true);
  1493.     return;
  1494.  
  1495. NoString:
  1496. NoRect:
  1497. NoControl:
  1498.     CloseMyWindow(deactivation);
  1499. NoWindow:
  1500.     AlertUser(MemError(), eNoWindow, kFatalError);
  1501. }
  1502.  
  1503. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1504. void DrawDeactivationWindow(DeactivationWindowPtr deactivation)
  1505. {
  1506.     TextFont(systemFont);
  1507.     TextSize(kSystemFontSize);
  1508.     HLock((Handle)deactivation->message);
  1509.     TextBox(*deactivation->message + 1, **deactivation->message,
  1510.             &deactivation->msgRect, teJustLeft);
  1511.     HUnlock((Handle)deactivation->message);
  1512.     FrameRect(&deactivation->passwordRect);
  1513.     DrawPassword(deactivation->passwordRect, Length(deactivation->password));
  1514.     DrawCountDown((WindowPtr)&deactivation->window, deactivation->delayPt,
  1515.                     &deactivation->lastTickCount, &deactivation->secondsCountDown);
  1516.     UpdateControls((WindowPtr)deactivation, ((WindowPtr)deactivation)->visRgn);
  1517.     DeviceLoop(((WindowPtr)deactivation)->visRgn, (DeviceLoopDrawingProcPtr)OutlineControl,
  1518.                 (long)(FindMyControl((WindowPtr)deactivation, kDeactivationOK)), 0);
  1519. }
  1520.  
  1521. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1522. void DeactivationKeyDown(DeactivationWindowPtr deactivation, unsigned char key, short modifiers)
  1523. {
  1524. #pragma unused (modifiers)
  1525.  
  1526.     unsigned char        length;
  1527.  
  1528.     switch (key) {
  1529.  
  1530.         case charBackspace:
  1531.             *deactivation->password = 0;
  1532.             SetPort((WindowPtr)deactivation);
  1533.             DrawPassword(deactivation->passwordRect, 0);
  1534.             break;
  1535.  
  1536.         case charEnter:
  1537.         case charReturn:
  1538.             SelectButton(FindMyControl((WindowPtr)deactivation, kDeactivationOK));
  1539.             DeactivationOKButton(deactivation);
  1540.             break;
  1541.  
  1542.         default:
  1543.             length = LimitStringLength(deactivation->password, kLengthOfPassword);
  1544.             deactivation->password[length] = key;
  1545.             SetPort((WindowPtr)deactivation);
  1546.             DrawPassword(deactivation->passwordRect, length);
  1547.             break;
  1548.     }
  1549. }
  1550.  
  1551. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1552. Boolean IsPasswordValid(DeactivationWindowPtr deactivation)
  1553. {
  1554.     StringHandle        newPassword;
  1555.  
  1556.     newPassword = (StringHandle)GetResource(kPasswordResType, kPasswordResID);
  1557.     FailIf(newPassword == nil, Failure);
  1558.     return (IUEqualString(deactivation->password, *newPassword) == 0);
  1559.  
  1560. Failure:
  1561.     return (false);
  1562. }
  1563.  
  1564. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1565. void DeactivationOKButton(DeactivationWindowPtr deactivation)
  1566. {
  1567.     OSErr            err;
  1568.  
  1569.     if (IsPasswordValid(deactivation))
  1570.         DeactivateAlarm();
  1571.     else {
  1572.         err = PlaySound(kAlarmTriggeredSnd);
  1573.         FailIf(err != noErr, Failure);
  1574.         *deactivation->password = 0;
  1575.         DrawPassword(deactivation->passwordRect, 0);
  1576.     }
  1577.  
  1578. Failure:
  1579.     return;
  1580. }
  1581.  
  1582. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1583. void DeactivationWindowClick(DeactivationWindowPtr deactivation, EventRecord *event)
  1584. {
  1585.     Point            mouse;
  1586.     ControlHandle    control;
  1587.  
  1588.     mouse = event->where;                            // get the click position
  1589.     GlobalToLocal(&mouse);
  1590.  
  1591.     if (FindControl(mouse, (WindowPtr)deactivation, &control)) {
  1592.         if (TrackControl(control, mouse, nil)) {
  1593.  
  1594.             switch (GetCRefCon(control)) {
  1595.  
  1596.                 case kDeactivationOK:
  1597.                     DeactivationOKButton(deactivation);
  1598.                     break;
  1599.  
  1600.             }
  1601.         }
  1602.     }
  1603. }
  1604.  
  1605. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1606. //    Calculate a sleep value for WaitNextEvent. This takes into account the things
  1607. //    that DoIdle does with idle time which happens if the alarm is activated.
  1608.  
  1609. long GetSleep(void)
  1610. {
  1611.     if (gAlarmState > kDeactivatedState)
  1612.         return (kAlarmedSleep);
  1613.     else
  1614.         return (kNotAlarmedSleep);
  1615. }
  1616.  
  1617.  
  1618.